// 仅运行于服务器
import { createApp } from './app';

export default context => {
  // 因为有可能会是异步路由钩子函数或组件，所以返回一个 Promise，以便服务器能够等待所有的内容在渲染前，就已经准备就绪。
  return new Promise((resolve, reject) => {
    const { app, router } = createApp;

    // 跳转到对应的路由
    router.push(context.url);

    // 等到 router 将可能的异步组件和钩子函数解析完
    router.onReady(() => {
      // 返回目标位置或是当前路由匹配的组件数组 (是数组的定义/构造类，不是实例)。通常在服务端渲染的数据预加载时使用
      const matchedComponents = router.getMatchedComponents();

      // 匹配不到的路由，执行 reject 函数，并返回 404
      if (!matchedComponents.length) {
        return reject({
          code: 404
        });
      }

      // Promise 应该 resolve 应用程序实例，以便它可以渲染
      // resolve(app);

      // 服务端数据预存
      // 对所有匹配的路由组件调用 asyncData()
      Promise.all(matchedComponents.map(Component => {
        if (Component.asyncData) {
          return Component.asyncData({
            store,
            route: router.currentRoute
          });
        }
      })).then(() => {
        // 在所有预取钩子(preFetch hook) resolve 后，
        // 我们的 store 现在已经填充入渲染应用程序所需的状态。
        // 当我们将状态附加到上下文，
        // 并且 `template` 选项用于 renderer 时，
        // 状态将自动序列化为 `window.__INITIAL_STATE__`，并注入 HTML。
        // 当使用 template 时，context.state 将作为 window.INITIAL_STATE 状态，自动嵌入到最终的 HTML 中
        context.state = store.state;
        resolve(app);
      }).catch(reject)
    }, reject);
  });
}